package com.lab303.powerproject.util;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

@Component
public class ColdStrategy {

    HashMap<String,List<Double> > conversionSet  = new HashMap<String, List<Double> >();//存储边与属性值集合的对应关系
    HashMap<Integer,String> id2str  = new HashMap<Integer,String>();//建立id与设备名称的对应关系
    HashMap<Integer,String> idx2path  = new HashMap<Integer,String>();//建立策略id与实际路径的对应关系
    List<Double>  Cost  = new ArrayList<Double>();//路径的电费数组
    double dayPrice = 0.7033,nightPrice = 0.9888;
    static float temprature = 33; //当外界环境温度上升到33C时候，地埋管供能能力折损30%
    /*当前设置成定制，实际情况:
      峰段（08:00-11:00 18:00-23:00）0.9888元；谷段（23:00-07:00）0.4338元；平段（07:00-08:00 11:00-18:00）0.7033元。
     */
    double BuriedTubeLimit = 400000;//地埋管最大功能能力，单位焦耳
    static String name = "data.json";
    double kwh2j = 3.6*10e6;
    private static final Logger LOG = LoggerFactory.getLogger(ColdStrategy.class);


    /*
     * 所有涉及能量的单位都是: 焦耳，
     * 所有涉及费用的单位都是:元
     * 转换率为在(0,1)的一个double型数据
     * p2prate 有向图中不同节点转移能量转换率，越大说明能量利用率越高。
     * e2prate 有向图中某个设备释放100J的能量消耗的电能，越大说明越费电。
     * */

    public  String getData(String fileName) throws IOException {
        ClassPathResource resource = new ClassPathResource(fileName);
        InputStream  input =   resource.getInputStream();
        StringBuffer out = new StringBuffer();
        byte[] b = new byte[4096];
        for (int n; (n = input.read(b)) != -1;) {
            out.append(new String(b, 0, n));
        }
        return out.toString();
    }

    public String getCurDay(){
        Date dt = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(dt);
    }
    public void tempratureCondition(float tem) throws IOException {
        String s = getData("\\3rd\\aj\\data\\ycnl.json");
        LOG.info("\\3rd\\aj\\data\\ycnl.json");
        if(s.length() == 0){
            LOG.error("读取json文件异常");
        }
        JSONObject jobj=new JSONObject(s);
        JSONObject nl = jobj.getJSONObject("nl");
        String day = getCurDay();
        LOG.info(day);
        if(nl.get(day)!= null)BuriedTubeLimit = (double)nl.get(day)*kwh2j;
        if (tem > temprature)
            BuriedTubeLimit*=0.7;
    }

    public void setGraph() throws JSONException, IOException {
        String s = getData(name);
        LOG.info(name);
        if(s.length() == 0){
            LOG.error("读取json文件异常");
        }
        JSONObject jObject1=new JSONObject(s);
        JSONArray jsonArray1=jObject1.getJSONArray("edge");
        JSONArray jsonArray2=jObject1.getJSONArray("p2prate");
        JSONArray jsonArray3=jObject1.getJSONArray("e2prate");

        JSONArray chsname=jObject1.getJSONArray("chsname");
        JSONArray graph=jObject1.getJSONArray("graph");

        for(int i = 0;i<jsonArray1.length();++i){
            List<Double> ld = new ArrayList<>();
            ld.add((Double)jsonArray2.get(i));
            ld.add((Double)jsonArray3.get(i));
            conversionSet.put((String)jsonArray1.get(i),ld);
        }

        for(int i = 0;i<chsname.length();++i)id2str.put(i,chsname.getString(i));
        for(int i = 0;i<graph.length();++i)idx2path.put(i,graph.getString(i));
    }

    public String prettyPrint(String input){
        String result = "";
        String str[] = input.split(",");
        for(int i =0;i<str.length;++i){
            String ss[] =  str[i].split("#");
            for(int j = ss.length - 1;j>=0;--j){
                if(j != ss.length - 1)result+="->";
                result+=id2str.get(Integer.parseInt(ss[j]));
            }
        }
        return result;
    }

    public  double getH2oStockLimit(){
        //获取水蓄能最大蓄能能力
        return 2000;
    }

    public double getNeedFromPrediction(){
        //地板需要的冷能；单位焦耳
        return 2000;
    }

    public double getFreePath(double x,boolean flag){
        //设置地板到免费冷这条功能路径上的cost,受地埋管最大蓄能能力影响
        double p2prate01 = conversionSet.get("0#1").get(0);
        double e2prate01 = conversionSet.get("0#1").get(1);

        double p2prate15 = conversionSet.get("1#5").get(0);
        double e2prate15 = conversionSet.get("1#5").get(1);
        if(flag&&BuriedTubeLimit*p2prate15*p2prate01 < x )return -1;//免费冷单独功能不能满足需要
        double fcost = (x/p2prate01 *e2prate01 + x/p2prate01/p2prate15 *e2prate15)*dayPrice;
        return fcost;
    }

    public double getWaterStock(double x,boolean flag){
        //水蓄能在晚上开启，相对省电。受水蓄能蓄能能力受地埋管最大蓄能能力影响
        double p2prate02 = conversionSet.get("0#2").get(0);
        double e2prate02 = conversionSet.get("0#2").get(1);

        double p2prate24 = conversionSet.get("2#4").get(0);
        double e2prate24 = conversionSet.get("2#4").get(1);

        double p2prate45 = conversionSet.get("4#5").get(0);
        double e2prate45 = conversionSet.get("4#5").get(1);
        if(flag&&getH2oStockLimit()*p2prate02<x)return -1;
        if(flag&&BuriedTubeLimit*p2prate02*p2prate24*e2prate45 <x)return -1;

        double wcost = (x/p2prate02 * e2prate02 + x/p2prate02/p2prate24*e2prate24 + x/p2prate02/p2prate24/p2prate45*e2prate45)*nightPrice;
        return wcost;
    }

    public double getAHostGrp(double x){
        //A机组可以通过制冷剂将地埋管抽上来的水进一步制冷，提供冷能。不受地埋管最大蓄能能力限制
        double p2prate03 = conversionSet.get("0#3").get(0);
        double e2prate03 = conversionSet.get("0#3").get(1);
        double p2prate35 = conversionSet.get("3#5").get(0);
        double e2prate35 = conversionSet.get("3#5").get(1);
        double acost = (x/p2prate03*e2prate03 + x/p2prate03/p2prate35*e2prate35)*dayPrice;
        return acost;
    }

    //组合策略01 (免费冷 + 水蓄能)
    public double combine01(double x){
        double needEnergy = 1e4,c01cost = 0;
        double p2prate01 = conversionSet.get("0#1").get(0);
        double e2prate01 = conversionSet.get("0#1").get(1);

        double p2prate15 = conversionSet.get("1#5").get(0);
        double e2prate15 = conversionSet.get("1#5").get(1);

        double power0 =  BuriedTubeLimit*p2prate15*p2prate01;
        if(power0 + getH2oStockLimit() <x)return -1; //如果水蓄能满负荷 + 地埋管满负荷都不能满足x的能量需求，直接返回
        double  convert0 =  getFreePath(needEnergy,false),convert1 = getWaterStock(needEnergy,false);
        if( convert0< convert1){//说明优先使用免费冷
            if(Cost.get(0) != -1)return -1;//此时说明免费冷单独供冷满足要求，不需要组合策略
               /*说明免费冷不能单独满足供冷要求，此时需要免费冷 + 水蓄能 组合策略
                 尽可能最大限度的使用免费冷;
                */
            double power1 = Math.max(x - power0,0);
            c01cost = getFreePath(power0,false) + getWaterStock(power1,false);
            return c01cost;//3号策略
        }else{
            if(Cost.get(1) != -1)return -1;//此时说明水蓄能单独供冷满足要求，不需要组合策略
                /*说明水蓄能不能单独满足供冷要求，此时需要水蓄能 + 免费冷 组合策略
                 尽可能最大限度的使用水蓄能;
                */
            double power_w = getH2oStockLimit();//即水蓄能以满负荷去蓄能
            double power_f = Math.max(x - power_w,0);
            c01cost = getFreePath(power_f,false) + getWaterStock(power_w,false);
            return c01cost;//3号策略
        }
    }

    //组合策略02 (免费冷 + A机组)
    public double combine02(double x){
        double needEnergy = 1e4,c02cost = 0;
        double  convert0 =  getFreePath(needEnergy,false),convert2 = getAHostGrp(needEnergy);
        if(convert0<convert2){
            if(Cost.get(0) != -1)return -1;
            double p2prate01 = conversionSet.get("0#1").get(0);
            double e2prate01 = conversionSet.get("0#1").get(1);

            double p2prate15 = conversionSet.get("1#5").get(0);
            double e2prate15 = conversionSet.get("1#5").get(1);

            double power0 =  BuriedTubeLimit*p2prate15*p2prate01;
            double power2 = Math.max(x - power0,0);
            c02cost = getFreePath(power0,false) + getAHostGrp(power2);
            return c02cost;
        }else{
            c02cost =  getAHostGrp(x);
            return c02cost;//4号策略
        }
    }

    //组合策略02 (水蓄能 + A机组)
    public double combine12(double x){//策略5
        double needEnergy = 1e4,c12cost = 0;
        double  convert1 =  getWaterStock(needEnergy,false),convert2 = getAHostGrp(needEnergy);
        if(convert1 <convert2){
            if(Cost.get(1) != -1)return -1;
            double power1 = getH2oStockLimit();//即水蓄能以满负荷去蓄能
            double power2 = x - power1;
            c12cost = getWaterStock(power1,false) + getAHostGrp(power2);
            return c12cost;//5号策略
        }else{
            c12cost =  getAHostGrp(x);
            return c12cost;
        }
    }

    //组合策略012 (免费冷 + 水蓄能 + A机组)
    public double combine012(double x){//策略6
        double needEnergy = 1e4,c012cost = 0;

        double p2prate01 = conversionSet.get("0#1").get(0);
        double e2prate01 = conversionSet.get("0#1").get(1);

        double p2prate15 = conversionSet.get("1#5").get(0);
        double e2prate15 = conversionSet.get("1#5").get(1);

        if(Cost.get(0) == -1&&Cost.get(1) == -1){
            //只有在免费冷 + 水蓄能都开启还不够的时候，才考虑此策略
            double power0 =  BuriedTubeLimit*p2prate15*p2prate01;
            double power1 = getH2oStockLimit();
            double power2 = Math.max(0,x - power0 - power1);
            c012cost = getFreePath(power0,false) + getWaterStock(power1,false) + getAHostGrp(power2);
            return c012cost;//6号策略
        }
        return -1;
    }

    public String getAllPath() {
        for (int i = 0; i < 7; ++i) Cost.add(-1.0);
        double need = getNeedFromPrediction();//通过预测模型获得需要的冷量
        Cost.set(0, getFreePath(need, true));
        Cost.set(1, getWaterStock(need, true));
        Cost.set(2, getAHostGrp(need));
        Cost.set(3, combine01(need));
        Cost.set(4, combine02(need));
        Cost.set(5, combine12(need));
        Cost.set(6, combine012(need));

        double mincost = 1e12;
        int idx = -1;
        for (int i = 0; i < Cost.size(); ++i) {
            if (Cost.get(i) != -1) {
                if (Cost.get(i) < mincost) {
                    mincost = Cost.get(i);
                    idx = i;
                }
            }
        }
        String input = idx2path.get(idx);
        return input;
    }


    public static void main(String[] args) throws JSONException, IOException {
        ColdStrategy ps = new ColdStrategy();
        ps.setGraph();
        ps.getAllPath();
    }

}
